package Derivation;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import TopK.EquationTopK;
import Basics.*;
import Circuit.Circuit;

public class ParseResultsBottomUp 
{
	DeriveByProgramBottomUp programDeriver;
	
	Map<Atom, HashSet<Body>> provenance;
	
	//HashSet<Rule> constRules;
	
	Map<String, Atom> keyMap;
	
	EquationTopK topk;
			
	Circuit c = new Circuit();

	Set<Atom> addedInstAtoms = new HashSet<Atom>();



	public ParseResultsBottomUp (Map<Atom, HashSet<Body>> iprov, Map<String, Atom> imap, Rule ... irs)
	{
		this.provenance = iprov;
		this.keyMap = imap;
		this.programDeriver = new DeriveByProgramBottomUp (this.provenance, this.keyMap, irs);
		//this.programDeriver.setProvenance(this.provenance);
		//this.programDeriver.setKeyMap(this.keyMap);
	}
	
	
	
	public ParseResultsBottomUp (Program p, Map<Atom, HashSet<Body>> iprov, Map<String, Atom> imap)
	{
		this.provenance = iprov;
		this.keyMap = imap;
		this.programDeriver = new DeriveByProgramBottomUp (this.provenance, this.keyMap, p, this.topk);
		
		//this.programDeriver.setProvenance(this.provenance);
		//this.programDeriver.setKeyMap(this.keyMap);
	}

	
	
	public ParseResultsBottomUp (Program p, Map<Atom, HashSet<Body>> iprov, Map<String, Atom> imap, EquationTopK itopk)
	{
		this.provenance = iprov;
		this.keyMap = imap;
		this.topk = itopk;
		this.programDeriver = new DeriveByProgramBottomUp (this.provenance, this.keyMap, p, this.topk);
		
		//this.programDeriver.setProvenance(this.provenance);
		//this.programDeriver.setKeyMap(this.keyMap);
	}


	
	public Set<Atom> getAddedInstAtoms() 
	{
		return addedInstAtoms;
	}

	

	public Circuit getC() 
	{
		return c;
	}
	
	

	public Map<Atom, HashSet<Body>> getProv() 
	{
		return provenance;
	}

	
	
	public Map<String, Atom> getKeyMap() 
	{
		return keyMap;
	}



	public void setKeyMap(Map<String, Atom> keyMap) 
	{
		this.keyMap = keyMap;
	}
	
	

	public DeriveByProgramBottomUp getProgramDeriver() 
	{
		return programDeriver;
	}



	public void setProgramDeriver(DeriveByProgramBottomUp programDeriver)
	{
		this.programDeriver = programDeriver;
	}



	/*************************************************************************************************************/
	/** Title: ParseResults																				
	/** Description: finds all newly derived atoms from this step and adds them to this.addedInstAtoms			
	/*************************************************************************************************************/

	public boolean ParseResults (boolean forIntersection, boolean online)
	{	
		//long startTime = System.currentTimeMillis();
		
		boolean retVal = false;
		Set<Atom> compareVec = new HashSet<Atom> (this.programDeriver.keyMap.values());//.getAtomsAddedSoFar());
		this.programDeriver.DeriveFromProgramIterationBottomUp(forIntersection, online);	
		this.addedInstAtoms = new HashSet<Atom> (this.programDeriver.keyMap.values());//.getAtomsAddedSoFar());
		if (false == compareVec.isEmpty()) //see if new atoms have been derived in this iteration
		{
			retVal = (compareVec.size() == this.addedInstAtoms.size());
		}
		
		long startTime = System.currentTimeMillis();
		if (false == retVal) 
		{
			this.addedInstAtoms.removeAll(compareVec); //for UpdateDB()
			startTime = System.currentTimeMillis();
		}
		
		else
		{
			this.programDeriver.getAtomsAddedSoFar().clear();
		}
		
		long end = System.currentTimeMillis();
		if ((end-startTime) > 100 && forIntersection) 
		{
			System.out.println("ParseResultsBottomUp:: time for circuit layer: " + (end-startTime));
			System.out.println("ParseResultsBottomUp:: num of added inst atoms: " + this.programDeriver.getAddedInstAtomsInLastIteration().size());
		}
		
		return retVal;
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: UpdateDB																				
	/** Description: add newly derived facts to DB			
	/*************************************************************************************************************/
	
	public void UpdateDB ()
	{
		for (Atom atom : this.addedInstAtoms) 
		{
			//DB.getInstance().Update(atom);
			MemDB.getInstance().Update(atom);
		}
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: Reset																				
	/** Description: resets the parameters for next step of the program			
	/*************************************************************************************************************/
	
	public void Reset ()
	{
		this.addedInstAtoms.clear();
		//this.programDeriver.Reset();
	}
	
	
	/*************************************************************************************************************/
	/** Title: AddToProv																				
	/** Description: 				
	/*************************************************************************************************************/
	
	public void AddToProv (Atom head, HashSet<Body> bodies)
	{
		if (false == this.provenance.containsKey(head))
		{
			this.provenance.put(head, new HashSet<Body>());
		}
		
		this.provenance.get(head).addAll(bodies);
		
		/*for (Body body : bodies) // make pointer from all body atoms to 
		{
			for (Atom atom : body.getAtoms()) 
			{
				if (false == atom.isFact()) 
				{
					atom.setKeyInProv(this.keyMap.get(atom.toString()));
				}
				
			}
		}*/
	}
	
	
	/*************************************************************************************************************/
	/** Title: GetProvSize																				
	/** Description: 				
	/*************************************************************************************************************/
	
	public int GetProvSize ()
	{
		int size = 0;
		for (HashSet<Body> set : this.provenance.values()) 
		{
			size += set.size();
		}
		
		return size;
	}
	
}
